const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";

const resolvePromise = (promise2, x, resolve, reject) => {
  // 自己等待自己完成是错误的实现，用一个类型错误，结束掉 promise  Promise/A+ 2.3.1
  if (promise2 === x) {
    return reject(new TypeError("Chaining cycle detected for promise #<Promise>"));
  }
  // Promise/A+ 2.3.3.3.3 只能调用一次
  let called;
  // 后续的条件要严格判断 保证代码能和别的库一起使用
  if ((typeof x === "object" && x != null) || typeof x === "function") {
    try {
      // 为了判断 resolve 过的就不用再 reject 了（比如 reject 和 resolve 同时调用的时候）  Promise/A+ 2.3.3.1
      let then = x.then;
      if (typeof then === "function") {
        // 不要写成 x.then，直接 then.call 就可以了 因为 x.then 会再次取值，Object.defineProperty  Promise/A+ 2.3.3.3

        /*
        * x.then调用的时候x会把value和reason传递进去，x的状态影响着返回Promise的状态的本质就是
        * new Promise().then要和x.then一样也就是要将x.then(f1,f2)传递给new Promise().then(f1,f2)
        *
        * */
        then.call(x, y => { // 根据 promise 的状态决定是成功还是失败
          if (called) return;
          called = true;
          // 递归解析的过程（因为可能 promise 中还有 promise） Promise/A+ 2.3.3.3.1
          resolvePromise(promise2, y, resolve, reject);
        }, r => {
          // 只要失败就失败 Promise/A+ 2.3.3.3.2
          if (called) return;
          called = true;
          reject(r);
        });
      } else {
        // 如果 x.then 是个普通值就直接返回 resolve 作为结果  Promise/A+ 2.3.3.4
        resolve(x);
      }
    } catch (e) {
      // Promise/A+ 2.3.3.2
      if (called) return;
      called = true;
      reject(e);
    }
  } else {
    // 如果 x 是个普通值就直接返回 resolve 作为结果  Promise/A+ 2.3.4
    resolve(x);
  }
};

class _Promise {
  constructor(executor) {
    /*
    * state变量其实是一个状态变量 状态变量可以赋予对象更多的选择空间
    * */
    this.status = PENDING;
    this.value = undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];

    let resolve = value => {
      if (this.status === PENDING) {
        this.status = FULFILLED;
        this.value = value;
        this.onResolvedCallbacks.forEach(fn => fn());
      }
    };

    let reject = reason => {
      if (this.status === PENDING) {
        this.status = REJECTED;
        this.reason = reason;
        this.onRejectedCallbacks.forEach(fn => fn());
      }
    };

    try {
      /*如果回调函数出错直接reject*/
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }

  /*
    * catch只会捕捉reject的情况 如果状态不是reject的话进行穿透
    *
    * 值穿透:
    *   在Promise的链式调用过程中如果当前的调用与当前的状态不符，或者函数参数传递的不是函数，比如new Promise中抛出错误那么应该找后面的
    *   catch调用或者reason函数，但是接下来的调用只有value调用，所以应该要"跳出"下一个调用，寻找后面合适的进行调用
    *
    * catch中的值穿透很简单,首先catch是只有在Promise的状态为Rejected的时候才进行调用，所以如果当前的Promise状态不是Rejected的话
    *
    *
    * */
  catch(err) {
     err = (typeof err !== "function") ||(this.status === FULFILLED) ? v=>v :err
      let promise2 = new _Promise((resolve, reject) => {
      setTimeout(() => {
        let x = this.status === FULFILLED ?  err(this.value):err(this.reason)
        resolvePromise(promise2, x, resolve, reject);
      });
    });

    return promise2;
  }

  finally(fn){
    let promise2 =new _Promise((resolve,reject)=>{

    })
    return promise2
  }


  /*
  *  then函数做的就是返回一个Promise对象
  *
  * */
  then(onFulfilled, onRejected) {
    //解决 onFulfilled，onRejected 没有传值的问题
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : v => v;

    /*
    * 如果错误的回调函数没有的话要返回一个抛出异常的函数，因为在下方使用了try...catch,
    * 捕获到异常之后直接调用reject将promise2的状态改为rejected，不走resolvePromise流程
    * */
    onRejected = typeof onRejected === "function" ? onRejected : err => {
      throw err;
    };

    let promise2 = new _Promise((resolve, reject) => {
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value);
            // x可能是一个proimise
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      }

      if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);

            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      }

      if (this.status === PENDING) {
        this.onResolvedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);

            }
          }, 0);
        });

        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
      }
    });

    return promise2;
  }
}